package com.zmn.oms.third.tc58.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.ltsopensource.core.domain.Job;
import com.zmn.base.common.data.common.dro.area.AreaDRO;
import com.zmn.base.common.data.dubbo.interfaces.area.AreaListRemoteService;
import com.zmn.base.product.common.dto.product.ec.ProductBaseDRO;
import com.zmn.base.product.dubbo.interfaces.channel.product.third.ChannelProductThirdListRemoteService;
import com.zmn.common.constant.GlobalConsts;
import com.zmn.common.dictionary.GlobalDict;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.amount.AmountUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.math.MathUtil;
import com.zmn.common.utils.math.MoneyUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.manager.lts.interfaces.task.TaskManager;
import com.zmn.oms.business.interfaces.log.OrderLogBService;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderLogConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.common.enums.ChannelFacade;
import com.zmn.oms.common.exception.OmsBaseException;
import com.zmn.oms.manager.utils.TaskConsts;
import com.zmn.oms.model.bo.order.OrderBO;
import com.zmn.oms.model.dto.order.proxy.*;
import com.zmn.oms.model.dto.product.OrderProductDTO;
import com.zmn.oms.model.entity.log.OrderLog;
import com.zmn.oms.model.entity.order.OrderExtend;
import com.zmn.oms.model.entity.work.OrderWork;
import com.zmn.oms.model.entity.work.OrderWorkLogChange;
import com.zmn.oms.model.vo.work.ZsOrderWorkVO;
import com.zmn.oms.services.interfaces.order.OrderExtendService;
import com.zmn.oms.services.interfaces.work.OrderWorkService;
import com.zmn.oms.third.base.dto.api.ApiInvokResult;
import com.zmn.oms.third.core.tracker.FacadeTracker;
import com.zmn.oms.third.tc58.Tc58ApiInvoker;
import com.zmn.oms.third.tc58.Tc58Consts;
import com.zmn.oms.third.tc58.Tc58Mapper;
import com.zmn.oms.third.tc58.dto.Tc58ExtOuterData;
import com.zmn.oms.third.tc58.dto.api.*;
import com.zmn.oms.third.tc58.service.Tc58Service;
import com.zmn.oms.zmn.aop.OrderWorkLogUtils;
import com.zmn.oms.zmn.business.interfaces.proxy.ProxyOrderBService;
import com.zmn.oms.zmn.business.interfaces.work.ZsOrderWorkQueryBService;
import com.zmn.oms.zmn.business.interfaces.work.autoflow.WorkFlowContextBService;
import com.zmn.pay.common.constant.PayConsts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 类描述：58同城接口调用实现类
 *
 * @author LiangHailong
 * @date 2019/09/24 16:17
 */
@Service
@Slf4j
public class Tc58ServiceImpl implements Tc58Service {
    @Autowired
    private TaskManager taskManager;

    @Autowired
    private OrderLogBService orderLogBService;
    @Autowired
    private OrderExtendService orderExtendService;
    @Autowired
    private ProxyOrderBService proxyOrderBService;
    @Autowired
    private WorkFlowContextBService workFlowContextBService;
    @Autowired
    private OrderWorkService orderWorkService;
    @Autowired
    protected ZsOrderWorkQueryBService zsOrderWorkQueryBService;
    @Reference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    protected AreaListRemoteService areaListRemoteService;
    @Reference(version = com.zmn.base.plat.engine.common.constant.DubboConsts.INTERFACE_VERSION , check = false)
    protected ChannelProductThirdListRemoteService channelProductThirdListRemoteService;

    /**
     * 保存订单
     * @param tc58Order
     */
    @Override
    public OrderBO saveOrder(Tc58Order tc58Order) throws Exception {
        if (tc58Order == null) {
            return null;
        }

        CreateOrderDTO createOrderDTO = buildZmnOrder(tc58Order);
        if (createOrderDTO == null) {
            throw new OmsBaseException("重复的订单数据");
        }

        OrderBO orderBO = proxyOrderBService.createOrder(createOrderDTO);
        OrderWork orderWork = orderBO.getOrderWork();
        log.info("【{}】保存订单成功 OrderId:[{}]", Tc58Consts.CHANNEL_NAME, orderWork.getOrderId());

        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker()
                .orderId(orderWork.getOrderId());

        // 存入工单日志
        saveOrderLog4PayInfo(orderWork);

        Tc58Consts.ChannelInfo channelInfo = Tc58Consts.getChannelInfo(orderWork.getChannelId());
        if (channelInfo.isJingxuan()) {
            // 精选渠道订单尚未获取到用户联系电话，不能自动流转
            addPrepayTimeoutCancelJob(orderWork.getOrderId());
        }
        else {
            // 工单自动流转
            workFlowContextBService.asyncAutoFlow(orderWork.getOrderId(),orderWork.getWorkId());
            tracker.remark("设置订单自动流转");
        }

        return orderBO;
    }

    /**
     * 修改订单状态（主要是订单完成和取消）
     * @param tc58OrderStatus
     */
    @Override
    public void changeOrderStatus(Tc58OrderStatus tc58OrderStatus) throws Exception {
        if (tc58OrderStatus == null) {
            return;
        }

        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        String outerId = tc58OrderStatus.getOrderId();
        // state：订单状态（1待接单, 2待服务, 3已完成, 4已关闭）
        String state = tc58OrderStatus.getState();

        if ("1".equals(state) || "2".equals(state)) {
            String stateName = "1".equals(state) ? "待接单" : "待服务";
            log.info("【{}】 修改订单状态，<{}>无需处理（已忽略）！OuterId:[{}]", Tc58Consts.CHANNEL_NAME, stateName, outerId);
            tracker.remark("修改订单状态【%s】无需处理（已忽略）", stateName);
            return;
        }

        ZsOrderWorkVO dbOrderWorkVO = zsOrderWorkQueryBService.findOrderWorkByOuterIdAndChannelIds(outerId, getTc58ChannelIdList());
        if (dbOrderWorkVO == null) {
            log.info("【{}】修改订单状态失败，未找到对应订单！OuterId:[{}]", Tc58Consts.CHANNEL_NAME, outerId);
            tracker.remark("修改订单状态失败：未找到对应订单");
            return;
        }

        Long orderId = dbOrderWorkVO.getOrderId();
        tracker.orderId(orderId);

        // 完成订单
        if ("3".equals(state)) {
            log.info("【{}】修改订单状态：<完成订单>无需处理（已忽略）！OuterId:[{}], OrderId:[{}]", Tc58Consts.CHANNEL_NAME,
                    outerId, orderId);
            tracker.remark("修改订单状态【订单完成】无需处理（已忽略）");

            {// 标记订单已收到渠道订单完成通知
                OrderExtend dbOrderExtend = orderExtendService.findMiniOrderExtendByOrderId(dbOrderWorkVO.getOrderId());
                if (dbOrderExtend != null) {
                    Tc58ExtOuterData tc58ExtOuterData = Tc58ExtOuterData.fromExtOuterData(dbOrderExtend.getExtOuterData());
                    tc58ExtOuterData.flagChannelCompleteNotice();

                    OrderExtend updateModel = new OrderExtend();
                    updateModel.setExtId(dbOrderExtend.getExtId());
                    updateModel.setExtOuterData(tc58ExtOuterData.toString());
                    orderExtendService.updateOrderExtend(updateModel);
                }
            }

            {// 记录操作日志
                OrderLogDTO orderLogDTO = new OrderLogDTO();
                orderLogDTO.setOperatorId(dbOrderWorkVO.getChannelId().longValue());
                orderLogDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
                orderLogDTO.setOrderId(orderId);
                orderLogDTO.setType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
                orderLogDTO.setOperatorRemark("渠道完成（已忽略，未做处理）");

                saveOrderLog(orderLogDTO, null);
            }
        }
        // 取消订单
        else if ("4".equals(state)) {
            log.info("【{}】修改订单状态：取消订单！OuterId:[{}], OrderId:[{}]", Tc58Consts.CHANNEL_NAME,
                    outerId, orderId);
            tracker.remark("修改订单状态【取消订单】");

            CancelOrderDTO cancelOrderDTO = new CancelOrderDTO();
            cancelOrderDTO.setChannelId(Tc58Consts.CHANNEL_ID_VIP);
            cancelOrderDTO.setOrderId(orderId);
            cancelOrderDTO.setOuterData(JSON.toJSONString(tc58OrderStatus));
            proxyOrderBService.cancelOrder(cancelOrderDTO);
        }
        else {
            log.info("【{}】未知的订单状态（已忽略）！OuterId:[{}], State:[{}]", Tc58Consts.CHANNEL_NAME, outerId, state);
            tracker.remark("修改订单状态：未知的订单状态（已忽略，State=[%s]）", state);
        }
    }

    /**
     * 保存二次支付信息
     * @param tc58SecondPayInfo
     */
    @Override
    public void saveSecondPayInfo(Tc58SecondPayInfo tc58SecondPayInfo) throws Exception {
        if (tc58SecondPayInfo == null) {
            return;
        }

        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        String outerId = tc58SecondPayInfo.getOrderId();
        ZsOrderWorkVO orderWorkVO = zsOrderWorkQueryBService.findOrderWorkByOuterIdAndChannelIds(outerId, getTc58ChannelIdList());
        if (orderWorkVO == null) {
            log.info("【{}】保存二次支付信息失败，未找到对应订单！OuterId:[{}]", Tc58Consts.CHANNEL_NAME, outerId);
            tracker.remark("保存二次支付信息失败，未找到对应订单!");
            return;
        }
        // ES同步有延迟，无法准确获取是否存在师傅收款额，所以需查主库 lhl-2021.01.04
        OrderWork orderWork = orderWorkService.findOrderWorkByKeySrcMaster(orderWorkVO.getOrderId(), orderWorkVO.getWorkId());
        tracker.orderId(orderWork.getOrderId());

        // state：二段支付状态: 1成功, 2失败
        String state = tc58SecondPayInfo.getState();
        if (!"1".equals(state)) {
            log.info("【{}】保存二次支付信息，支付失败，无需处理（已忽略）！OuterId:[{}]", Tc58Consts.CHANNEL_NAME, outerId);
            tracker.remark("消息中的二次支付状态为失败，无需处理（已忽略）！");
            return;
        }

        log.info("【{}】保存二次支付信息！OuterId:[{}], OrderId:[{}]", Tc58Consts.CHANNEL_NAME,
                outerId, orderWork.getOrderId());

        // 是否抵扣师傅收款额
        boolean subMasterAmount = true;
        if (Optional.ofNullable(orderWork.getMasterAmount()).orElse(0) <= 0) {
            // 不存在师傅收款额时（比如师傅上门报价前，用户在渠道方支付尾款），不抵扣师傅收款额，只记录渠道代收金额
            subMasterAmount = false;
        }

        PayOrderDTO payOrderDTO = new PayOrderDTO();
        payOrderDTO.setOrderPayType(OrderConsts.API_ORDER_PAY_TYPE_CHANNEL_MASTER_AMOUNT);
        payOrderDTO.setChannelId(orderWork.getChannelId());
        payOrderDTO.setOrderId(orderWork.getOrderId());
        payOrderDTO.setOuterData(JSON.toJSONString(tc58SecondPayInfo));
        payOrderDTO.setWorkId(orderWork.getWorkId());
        payOrderDTO.setPayAmount(MoneyUtil.parseToFen(Double.parseDouble(tc58SecondPayInfo.getPayAmount())));
        payOrderDTO.setPayTime(DateUtil.parse(Long.parseLong(tc58SecondPayInfo.getPayTime())));
        payOrderDTO.setSubMasterAmount(subMasterAmount);
        // 抵扣师傅收款额后，将订单置为完成状态，否则不更改订单状态
        payOrderDTO.setCompleteOrder(subMasterAmount);

        boolean isJingxuan = Tc58Consts.getChannelInfo(orderWork.getChannelId()).isJingxuan();
        int cash = 0;
        int couponAmount = 0;
        if (isJingxuan) {
            // 精选渠道二次支付时，客户在58端有可能使用优惠券，但二次支付消息中并不包含优惠金额字段。
            // 二次支付消息中包含两个价格字段：totalAmount（订单的实际总金额），payAmount（用户支付的现金金额，不含优惠券金额）。
            // 我们和58结算时，以totalAmount金额为准，即，如果存在优惠券金额，由58方负担。
            // 所以，保存二次支付金额时，需要修正，将优惠金额包含进去。
            // 二次支付金额 = totalAmount - 录单时的渠道预付款 = payAmount + 优惠金额
            // 优惠金额 = totalAmount - 录单时的渠道预付款 - payAmount
            String totalAmountStr = tc58SecondPayInfo.getTotalAmount();
            Integer totalAmount = MoneyUtil.parseToFen(Double.parseDouble(totalAmountStr));
            OrderWork dbOrderWork = orderWorkService.findOrderWorkByKey(orderWork.getOrderId(), orderWork.getWorkId());
            Integer channelPrepayAmount = dbOrderWork.getChannelPrepayAmount();
            Integer secondPayAmount = MathUtil.sub(totalAmount, channelPrepayAmount);

            cash = payOrderDTO.getPayAmount();
            couponAmount = MathUtil.sub(secondPayAmount, new Integer(cash));

            payOrderDTO.setPayAmount(secondPayAmount);
        }

        StringBuilder remarkBuilder = new StringBuilder();
        if (payOrderDTO.getOperatorRemark() != null) {
            remarkBuilder.append(payOrderDTO.getOperatorRemark());
        }

        Integer fristPayAmount = Optional.ofNullable(orderWork.getChannelPrepayAmount()).orElse(0);
        remarkBuilder.append("订单已预付").append(MoneyUtil.parseToYuanStr(fristPayAmount)).append("元，")
                .append("二次预付").append(MoneyUtil.parseToYuanStr(payOrderDTO.getPayAmount())).append("元；");
        if (isJingxuan) {
            remarkBuilder.append("用户二次支付包含现金：").append(MoneyUtil.parseToYuanStr(cash)).append("元，")
                    .append("优惠券：").append(MoneyUtil.parseToYuanStr(couponAmount)).append("元；");
        }
        if (!subMasterAmount) {
            remarkBuilder.append("师傅未报价，本次支付不抵扣师傅收款额；");
        }

        payOrderDTO.setOperatorRemark(remarkBuilder.toString());
        tracker.remark(payOrderDTO.getOperatorRemark());
        proxyOrderBService.payOrder(payOrderDTO);
    }

    /**
     * 修改预约时间
     * @param tc58ServiceTime
     */
    @Override
    public void changeServiceTime(Tc58ServiceTime tc58ServiceTime) throws Exception {
        if (tc58ServiceTime == null) {
            return;
        }

        Date serviceTime = DateUtil.parse(Long.parseLong(tc58ServiceTime.getServiceTime()));
        String serviceTimeStr = DateUtil.toString(serviceTime);

        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker();

        String outerId = tc58ServiceTime.getOrderId();
        ZsOrderWorkVO orderWork = zsOrderWorkQueryBService.findOrderWorkByOuterIdAndChannelIds(outerId, getTc58ChannelIdList());
        if (orderWork == null) {
            log.info("【{}】客户修改预约时间失败，未找到对应订单！OuterId:[{}]", Tc58Consts.CHANNEL_NAME, outerId);
            throw new OmsBaseException("未找到对应订单");
        }

        tracker.orderId(orderWork.getOrderId());

        if (orderWork.getStatus().intValue() >= OrderStatusConsts.WORK_STATUS_COMPLETE
                || orderWork.getResultStatus() != OrderStatusConsts.WORK_RESULT_DOING  ) {
            throw new OmsBaseException("当前订单不可修改预约时间");
        }

        OrderLog orderLog = new OrderLog();

        orderLog.setOrderId(orderWork.getOrderId());
        orderLog.setWorkId(orderWork.getWorkId());
        orderLog.setContent(String.format("客户修改预约时间：%s；", serviceTimeStr));

        orderLog.setType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
        orderLog.setTypeName(OrderLogConsts.getOrderLogTypeName(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK));
        orderLog.setBeforeStatus(orderWork.getStatus());
        orderLog.setBeforeStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
        orderLog.setBeforeResultStatus(orderWork.getResultStatus());
        orderLog.setBeforeResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));
        orderLog.setAfterStatus(orderWork.getStatus());
        orderLog.setAfterStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
        orderLog.setAfterResultStatus(orderWork.getResultStatus());
        orderLog.setAfterResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));

        OrderWorkLogUtils.processOrderLogOperater(orderLog, null);

        tracker.remark(orderLog.getContent());
        orderLogBService.save(orderLog);
    }

    /**
     * 激活精选渠道订单（获取用户联系电话，并将订单置为可操作状态）
     */
    @Override
    public void activeJingxuanOrder(OrderWorkLogChange orderWorkLogChange) {
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker()
                .dataOut(ChannelFacade.TONGCHENG_58)
                .remark("抢单-精选渠道订单")
                .orderId(orderWorkLogChange.getOrderId());

        Tc58ExtOuterData tc58ExtOuterData = Tc58ExtOuterData.fromExtOuterData(orderWorkLogChange.getExtOuterData());
        Tc58Order tc58Order = Tc58ApiInvoker.queryOrderInfo(orderWorkLogChange.getOuterId(), true,
                tc58ExtOuterData.getBUserId());
        if (tc58Order != null && StringUtils.isNotBlank(tc58Order.getPhone())) {
            tracker.success(true, "获取客户联系电话-成功；");
            try {
                UpdateOrderDTO updateOrderDTO = new UpdateOrderDTO();
                updateOrderDTO.setOrderId(orderWorkLogChange.getOrderId());
                updateOrderDTO.setWorkId(orderWorkLogChange.getWorkId());
                updateOrderDTO.setPhone(tc58Order.getPhone());
                updateOrderDTO.setChannelPrepayStatus(PayConsts.PAY_STATUS_DONE);
                updateOrderDTO.setDisableDiscard(GlobalConsts.NO);
                proxyOrderBService.updateOrder(updateOrderDTO);
                tracker.remark("激活精选渠道订单-成功；");

                // 工单自动流转
                workFlowContextBService.asyncAutoFlow(orderWorkLogChange.getOrderId(), orderWorkLogChange.getWorkId());
                tracker.remark("设置订单自动流转-成功");
            } catch (OmsBaseException e) {
                tracker.error(e, e.getMessage());
                log.error(e.getMessage(), e);
            }
        }
        else {
            tracker.success(false, "请求客户联系电话-失败；");
        }
        tracker.commit();
    }

    @Override
    public String activeJingxuanOrder(Long orderId) throws OmsBaseException {
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker()
                .dataOut(ChannelFacade.TONGCHENG_58)
                .remark("抢单-精选渠道订单")
                .orderId(orderId);

        OrderWork orderWork = orderWorkService.findOrderWorkByKey(orderId,orderId);
        OrderExtend orderExtend = orderExtendService.findMiniOrderExtendByOrderId(orderId);
        Tc58ExtOuterData tc58ExtOuterData = Tc58ExtOuterData.fromExtOuterData(orderExtend.getExtOuterData());

        Tc58Order tc58Order = Tc58ApiInvoker.queryOrderInfo(
                orderWork.getOuterId(), true, tc58ExtOuterData.getBUserId()
        );

        String phone = null;
        if (tc58Order != null && StringUtils.isNotBlank(tc58Order.getPhone())) {
            try {
                tracker.success(true, "请求客户联系电话-成功；");

                phone = tc58Order.getPhone();
                UpdateOrderDTO updateOrderDTO = new UpdateOrderDTO();
                updateOrderDTO.setOrderId(orderWork.getOrderId());
                updateOrderDTO.setWorkId(orderWork.getWorkId());
                updateOrderDTO.setPhone(phone);
                updateOrderDTO.setChannelPrepayStatus(PayConsts.PAY_STATUS_DONE);
                updateOrderDTO.setDisableDiscard(GlobalConsts.NO);
                proxyOrderBService.updateOrder(updateOrderDTO);
                tracker.remark("激活精选渠道订单-成功；");

                // 工单自动流转
                workFlowContextBService.asyncAutoFlow(orderWork.getOrderId(), orderWork.getWorkId());
                tracker.remark("设置订单自动流转-成功");
            } catch (OmsBaseException e) {
                tracker.error(e, e.getMessage()).success(false).commit();
                log.error(e.getMessage(), e);
                throw e;
            }
        }
        else {
            tracker.success(false, "获取客户联系电话失败；");
        }
        tracker.commit();

        return phone;
    }

    @Override
    public ApiInvokResult findJingxuanPayUrl(Long orderId, String outerId, Double orderMoney) throws OmsBaseException {
        FacadeTracker.FacadeTrackerCore tracker = FacadeTracker.getTracker()
                .dataOut(ChannelFacade.TONGCHENG_58)
                .orderId(orderId)
                .remark("获取渠道支付链接");

        ApiInvokResult<Tc58PayLink> result = Tc58ApiInvoker.payLink(outerId, orderMoney, true);

        tracker.success(result.isSuccess()).commit();

        return result;
    }


    /**
     * 构造啄木鸟保存工单对象
     * @param tc58Order
     * @return
     * @throws OmsBaseException
     */
    private CreateOrderDTO buildZmnOrder(Tc58Order tc58Order) throws OmsBaseException {
        Tc58Consts.ChannelInfo channelInfo = Tc58Consts.getChannelInfo(tc58Order);
        if (channelInfo.isJingxuan()) {
            return _buildOrder4Jingxuan(tc58Order, channelInfo.getId(), channelInfo.getName());
        }
        else {
            return _buildOrder4VipOrFwk(tc58Order, channelInfo.getId(), channelInfo.getName());
        }
    }

    /**
     * 构造啄木鸟保存工单对象：针对“58同城114”渠道和“58同城服务卡”渠道
     * @param tc58Order
     * @return
     */
    private CreateOrderDTO _buildOrder4VipOrFwk(Tc58Order tc58Order, int channelId, String channelName) throws OmsBaseException {
        String outerId = tc58Order.getOrderId();

        // 校验是不是重复的订单消息
        ZsOrderWorkVO orderWork = zsOrderWorkQueryBService.findOrderWorkByOuterId(outerId, channelId);
        if (orderWork != null) {
            log.info("【{}】重复的消息数据（已忽略） OuterId:[{}]", channelName, outerId);
            return null;
        }

        CreateOrderDTO createOrderDTO = new CreateOrderDTO();
        createOrderDTO.setOuterData(JSON.toJSONString(tc58Order));
        createOrderDTO.setChannelId(channelId);

        // 将同步数据中的订单关键信息记录进日志中
        createOrderDTO.setOperatorLogRemark(String.format(
                "订单编号：%s，订单信息：产品：%s-%s-%s，数量：%s，单价：%s元；",
                outerId, tc58Order.getCateOne(), tc58Order.getCateTwo(), tc58Order.getCateThree(),
                tc58Order.getAmount(), tc58Order.getPrice()));

        createOrderDTO.setOuterId(outerId);
        createOrderDTO.setContactName(tc58Order.getCustomerName());
        createOrderDTO.setTelephone(tc58Order.getPhone());
        createOrderDTO.setRemark(tc58Order.getRemark());

        // expectDate 预约时间，注意就近转化为：整点和半点
        String expectDate = tc58Order.getServeTime();
        if (StringUtils.isNotBlank(expectDate)) {
            Long expectDateLong = Long.parseLong(expectDate); // 单位：秒
            Date dutyTime = new Date(expectDateLong * 1000); // 需转换成毫秒
            // 忽略掉当前时间超过预约时间的情况，由线下去确定预约时间
            if (dutyTime.getTime() > System.currentTimeMillis()) {
                createOrderDTO.setDutyTime(DateUtil.round(dutyTime, DateUtil.UNIT_MINUTE));
            }
        }

        try {
            // 地址转换
            _fillAddress(createOrderDTO, tc58Order);
            // 商品转换
            _fillProductInfo(createOrderDTO, tc58Order,channelId);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new OmsBaseException("产品/地址信息有误！");
        }

        createOrderDTO.setPlat(GlobalConsts.PLAT_MARK_ZMN);
        createOrderDTO.setGender(GlobalDict.GENDER_NO);
        createOrderDTO.setDisableDiscard(GlobalConsts.NO);
        createOrderDTO.setOperator("系统");
        createOrderDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
        createOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
        createOrderDTO.setInputType(OrderConsts.ORDER_INPUT_TYPE_API);

        return createOrderDTO;
    }

    /**
     * 构造啄木鸟保存工单对象：针对58同城“到家精选”渠道
     * @param tc58Order
     * @return
     */
    private CreateOrderDTO _buildOrder4Jingxuan(Tc58Order tc58Order, int channelId, String channelName) throws OmsBaseException {
        String outerId = tc58Order.getOrderId();

        // 校验是不是重复的订单消息
        ZsOrderWorkVO orderWork = zsOrderWorkQueryBService.findOrderWorkByOuterId(outerId, channelId);
        if (orderWork != null) {
            log.info("【{}】重复的消息数据（已忽略） OuterId:[{}]", channelName, outerId);
            return null;
        }

        CreateOrderDTO createOrderDTO = new CreateOrderDTO();
        createOrderDTO.setOuterData(JSON.toJSONString(tc58Order));
        createOrderDTO.setChannelId(channelId);

        // 将同步数据中的订单关键信息记录进日志中
        createOrderDTO.setOperatorLogRemark(String.format(
                "订单编号：%s，订单信息：产品：%s-%s-%s，数量：%s，单价：%s元，预付：%s元；",
                outerId, tc58Order.getCateOne(), tc58Order.getCateTwo(), tc58Order.getCateThree(),
                tc58Order.getAmount(), tc58Order.getPrice(), tc58Order.getTotalAmount()));

        createOrderDTO.setOuterId(outerId);
        createOrderDTO.setContactName(tc58Order.getCustomerName());
        createOrderDTO.setRemark(tc58Order.getRemark());

        Tc58ExtOuterData tc58ExtOuterData = new Tc58ExtOuterData();
        tc58ExtOuterData.setBUserId(tc58Order.getBUserId());
        createOrderDTO.setExtOuterData(tc58ExtOuterData.toString());

        // expectDate 预约时间，注意就近转化为：整点和半点
        String expectDate = tc58Order.getServeTime();
        if (StringUtils.isNotBlank(expectDate)) {
            Long expectDateLong = Long.parseLong(expectDate); // 单位：秒
            Date dutyTime = new Date(expectDateLong * 1000); // 需转换成毫秒
            // 忽略掉当前时间超过预约时间的情况，由线下去确定预约时间
            if (dutyTime.getTime() > System.currentTimeMillis()) {
                createOrderDTO.setDutyTime(DateUtil.round(dutyTime, DateUtil.UNIT_MINUTE));
            }
        }

        try {
            // 地址转换
            _fillAddress4Jingxuan(createOrderDTO, tc58Order);
            // 产品转换
            _fillProductInfo(createOrderDTO, tc58Order, channelId);

            // 精选渠道需要先给对方同步接单状态，才能获取用户联系手机号，
            // 所以录单时，手机号临时随便填一个，将订单置为付款中，不可操作状态（不能确认、弃单等），
            // 待同步完接单状态，并获取到手机号，再将订单置为可操作状态。
            createOrderDTO.setTelephone(OrderConsts.CHANNEL_TEMP_MOBILE);
            createOrderDTO.setChannelPrepayStatus(PayConsts.PAY_STATUS_ING);
            createOrderDTO.setDisableDiscard(GlobalConsts.YES);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new OmsBaseException("产品/地址信息有误！");
        }

        createOrderDTO.setPlat(GlobalConsts.PLAT_MARK_ZMN);
        createOrderDTO.setGender(GlobalDict.GENDER_NO);
        createOrderDTO.setOperator("系统");
        createOrderDTO.setOperatorId((long) GlobalConsts.OPERATE_USER_TYPE_SYSTEM);
        createOrderDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
        createOrderDTO.setInputType(OrderConsts.ORDER_INPUT_TYPE_API);

        return createOrderDTO;
    }

    /**
     * 补全产品信息
     * @param zmnOrder
     * @param tc58Order
     */
    private void _fillProductInfo(CreateOrderDTO zmnOrder, Tc58Order tc58Order, Integer channelId) {
        // 操作日志添加58产品信息
        StringBuilder product58 = new StringBuilder();
        product58.append(";58产品信息：")
                .append(tc58Order.getCateOne())
                .append("-")
                .append(tc58Order.getCateThree());
        // 产品
        OrderProductDTO orderProduct = new OrderProductDTO();
        // 渠道预付款
        Integer channelPrepayAmount = null;
        if (Tc58Consts.ORDER_SOURCE_TYPE_JX.equals(tc58Order.getSourceType())) {
            ResponseDTO<List<ProductBaseDRO>> servProductListResponseDTO = channelProductThirdListRemoteService
                    .listByChannelIdAndProductParentIdAndSubId(channelId, tc58Order.getSkuId(), null);
            Optional<ProductBaseDRO> productOptional = Optional.ofNullable(servProductListResponseDTO)
                    .filter(ResponseDTO::isSuccess)
                    .map(ResponseDTO::getData)
                    .map(list -> list.isEmpty() ? null : list.get(0));
            if (!productOptional.isPresent()) {
                log.info(
                        "【{}】调用#channelProductMapListRemoteService#listByChannelIdAndProductParentIdAndProductSubId#失败，参数【{}】返回结果【{}】",
                        Tc58Consts.CHANNEL_NAME,
                        tc58Order.getSkuId(),
                        servProductListResponseDTO.toString()
                );
            }
            Integer productId = productOptional
                    .map(ProductBaseDRO::getProductId)
                    .orElse(OrderConsts.CHANNEL_DISABLED_PRODUCT_ID);

            orderProduct.setProductId(productId);

            product58.append(", skuId=").append(tc58Order.getSkuId());

            Double priceDouble = Optional.ofNullable(tc58Order.getTotalAmount()).orElse(0d); // 价格单位用：元
            channelPrepayAmount = MathUtil.movePointRight(priceDouble.toString(), 2); // 订单总价，单位转成分
        } else {
            orderProduct.setProductId(Tc58Mapper.getZmnProductId(
                    tc58Order.getCateOneId(), tc58Order.getCateTwoId(), tc58Order.getCateThreeId()));

            Double priceDouble = Optional.ofNullable(tc58Order.getPrice()).orElse(0d); // 价格单位用：元
            channelPrepayAmount = MathUtil.movePointRight(priceDouble.toString(), 2); // 订单总价，单位转成分
        }

        // 产品数量
        String countStr = Optional.ofNullable(tc58Order.getAmount()).orElse("1");
        Integer count = Integer.parseInt(countStr);
        orderProduct.setNumber(count);
        List<OrderProductDTO> list = new LinkedList<>();
        list.add(orderProduct);
        zmnOrder.setOrderProductList(list);

        zmnOrder.setChannelPrepayAmount(channelPrepayAmount);
        zmnOrder.setChannelPrepayStatus(PayConsts.PAY_STATUS_DONE);
        zmnOrder.setOperatorLogRemark(zmnOrder.getOperatorLogRemark() + product58);
    }

    /**
     * 补全订单地址
     * @param orderDTO
     * @param tc58Order
     */
    private void _fillAddress4Jingxuan(CreateOrderDTO orderDTO, Tc58Order tc58Order) {
        orderDTO.setStreet(Optional.ofNullable(tc58Order.getServerAddress()).orElse("")); // street字段在数据库中不能为空

        if (StringUtils.isNotBlank(tc58Order.getLongitude()) && StringUtils.isNotBlank(tc58Order.getLatitude())) {
            Double lng = Double.valueOf(tc58Order.getLongitude());
            Double lat = Double.valueOf(tc58Order.getLatitude());

            orderDTO.setLongitude(lng);
            orderDTO.setLatitude(lat);

            ResponseDTO<AreaDRO> areaResponse = areaListRemoteService.getAreaByLatAndLng(lng, lat);
            AreaDRO areaDRO = areaResponse.getData();
            if (areaDRO != null) {
                orderDTO.setCityId(areaDRO.getParentId());
            }
        }

        // 默认重庆
        if (orderDTO.getCityId() == null) {
            orderDTO.setCityId(500100);
        }

        // 记录一下完整地址
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(orderDTO.getOperatorLogRemark()).append(" ");
        stringBuilder.append(tc58Order.getCity()).append(tc58Order.getCommunity()).append(tc58Order.getHouseNumber());
        orderDTO.setOperatorLogRemark(stringBuilder.toString());
    }

    /**
     * 补全订单地址
     * @param orderDTO
     * @param tc58Order
     */
    private void _fillAddress(CreateOrderDTO orderDTO, Tc58Order tc58Order) {
        orderDTO.setAddress(tc58Order.getCommunity());
        orderDTO.setStreet(Optional.ofNullable(tc58Order.getHouseNumber()).orElse("")); // street字段在数据库中不能为空

        if (StringUtils.isNotBlank(tc58Order.getLongitude()) && StringUtils.isNotBlank(tc58Order.getLatitude())) {
            Double lng = Double.valueOf(tc58Order.getLongitude());
            Double lat = Double.valueOf(tc58Order.getLatitude());

            orderDTO.setLongitude(lng);
            orderDTO.setLatitude(lat);

            com.zmn.common.dto2.ResponseDTO<AreaDRO> areaResponse = areaListRemoteService.getAreaByLatAndLng(lng, lat);
            AreaDRO areaDRO = areaResponse.getData();
            if (areaDRO != null) {
                orderDTO.setCityId(areaDRO.getParentId());
            }
        }

        // 默认重庆
        if (orderDTO.getCityId() == null) {
            orderDTO.setCityId(500100);
        }

        // 记录一下完整地址
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(orderDTO.getOperatorLogRemark()).append(" ");
        stringBuilder.append(tc58Order.getCity()).append(tc58Order.getCommunity()).append(tc58Order.getHouseNumber());
        orderDTO.setOperatorLogRemark(stringBuilder.toString());
    }

    /**
     *
     * @param orderWork
     * @return
     */
    private void saveOrderLog4PayInfo(OrderWork orderWork) {
        Integer channelPrepayAmount = orderWork.getChannelPrepayAmount();
        Integer channelPrepayStatus = orderWork.getChannelSettleStatus();
        if (NumberUtil.isNotNullOrZero(channelPrepayAmount)
                && Objects.equals(PayConsts.PAY_STATUS_DONE, channelPrepayStatus)) {
            OrderLog orderLog = new OrderLog();

            orderLog.setOrderId(orderWork.getOrderId());
            orderLog.setWorkId(orderWork.getWorkId());
            orderLog.setContent(String.format("58支付信息：支付金额%.2f；", AmountUtil.fen2Yuan(channelPrepayAmount)));

            orderLog.setType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
            orderLog.setTypeName(OrderLogConsts.getOrderLogTypeName(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK));
            orderLog.setBeforeStatus(orderWork.getStatus());
            orderLog.setBeforeStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
            orderLog.setBeforeResultStatus(orderWork.getResultStatus());
            orderLog.setBeforeResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));
            orderLog.setAfterStatus(orderWork.getStatus());
            orderLog.setAfterStatusName(OrderStatusConsts.getWorkStatusName(orderWork.getStatus()));
            orderLog.setAfterResultStatus(orderWork.getResultStatus());
            orderLog.setAfterResultStatusName(OrderStatusConsts.getWorkResultStatusName(orderWork.getResultStatus()));

            OrderWorkLogUtils.processOrderLogOperater(orderLog, null);

            orderLogBService.save(orderLog);
        }
    }

    /**
     * 添加超时未支付检查任务
     * @param orderId
     */
    private void addPrepayTimeoutCancelJob(Long orderId) {
        Job job = new Job();
        job.setTaskId(TaskConsts.TASK_ID_CHANNEL_PAY_TIMEOUT_CANCEL + ":" + orderId);
        job.setParam(TaskManager.SHARD_FIELD, TaskConsts.TASK_ID_CHANNEL_PAY_TIMEOUT_CANCEL);
        job.setParam("orderId", orderId.toString());
        Date date = DateUtil.addMinute(DateUtil.getNow(), OrderConsts.ORDER_PREPAY_TIMEOUT_CANCEL_MINUTES);
        taskManager.submitTask(job, -1, DateUtil.dateFormatToString(date, DateUtil.FORMAT_DEFAULT));
    }

    /**
     * 获取58同城相关渠道
     * @return
     */
    private List<Integer> getTc58ChannelIdList() {
        List<Integer> channelIdList = new ArrayList<>();
        channelIdList.add(Tc58Consts.CHANNEL_ID_VIP);
        channelIdList.add(Tc58Consts.CHANNEL_ID_FWK);
        channelIdList.add(Tc58Consts.CHANNEL_ID_JX);
        channelIdList.add(Tc58Consts.CHANNEL_ID_JX_CLOCK);
        channelIdList.add(Tc58Consts.CHANNEL_ID_JX_PIPE);
        return channelIdList;
    }

    /**
     * 保存日志
     * @param orderLogDTO
     * @param dbOrderWork
     */
    private void saveOrderLog(OrderLogDTO orderLogDTO, OrderWork dbOrderWork) {
        try {
            Long orderId = orderLogDTO.getOrderId();

            if (dbOrderWork == null) {
                dbOrderWork = orderWorkService.findOrderWorkByKey(orderId, orderId);
            }

            if (orderLogDTO.getType() == null) {
                orderLogDTO.setType(OrderLogConsts.ORDER_LOG_TYPE_CHANNEL_REMARK);
            }
            if (orderLogDTO.getOperatorType() == null) {
                orderLogDTO.setOperatorType(GlobalConsts.OPERATE_USER_TYPE_CHANNEL);
                orderLogDTO.setOperator("渠道");
                orderLogDTO.setOperatorId(dbOrderWork.getChannelId().longValue());
            }

            OrderLog orderLog = new OrderLog();
            orderLog.setOrderId(orderId);
            orderLog.setWorkId(orderId);
            orderLog.setType(orderLogDTO.getType());
            orderLog.setTypeName(OrderLogConsts.getOrderLogTypeName(orderLogDTO.getType()));
            orderLog.setBeforeStatus(dbOrderWork.getStatus());
            orderLog.setBeforeStatusName(OrderStatusConsts.getWorkStatusName(dbOrderWork.getStatus()));
            orderLog.setBeforeResultStatus(dbOrderWork.getResultStatus());
            orderLog.setBeforeResultStatusName(OrderStatusConsts.getWorkResultStatusName(dbOrderWork.getResultStatus()));
            orderLog.setAfterStatus(dbOrderWork.getStatus());
            orderLog.setAfterStatusName(OrderStatusConsts.getWorkStatusName(dbOrderWork.getStatus()));
            orderLog.setAfterResultStatus(dbOrderWork.getResultStatus());
            orderLog.setAfterResultStatusName(OrderStatusConsts.getWorkResultStatusName(dbOrderWork.getResultStatus()));
            OrderWorkLogUtils.processOrderLogOperater(orderLog, orderLogDTO);

            StringBuilder sb = new StringBuilder();

            if (StringUtil.isNotBlank(orderLogDTO.getOperatorRemark())) {
                sb.append(orderLogDTO.getOperatorRemark());
            }

            if (StringUtil.isNotBlank(orderLogDTO.getOperatorLogRemark())) {
                sb.append(orderLogDTO.getOperatorLogRemark());
            }

            orderLog.setContent(sb.toString());
            orderLogBService.save(orderLog);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static void main(String[] args) {
        Tc58Consts.APPKEY = "1174937402810948372";  // 测试
        Tc58Consts.SECRET = "2uJ14qs0p8bjdte5385mqi3kdi8a217f"; // 测试
        Tc58Consts.API_URL = "https://bsteward.58.com"; // 测试

        String tc58OrderId = "1176384836710907904";

        Tc58ServiceImpl tc58Service = new Tc58ServiceImpl();
        //Tc58Order tc58Order = tc58Service.queryOrderInfo(tc58OrderId);
        //System.out.println(JSON.toJSONString(tc58Order));
        Double d = 2.04;
        System.out.println(MathUtil.movePointRight(d.toString(), 2));
    }

}
